//==============================================================================
//
//        DFSM - the finite state machine class in the ofc-library
//
//               Copyright (C) 2003  Dick van Oudheusden
//  
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//==============================================================================
// 
//  $Date: 2005-02-05 06:25:37 $ $Revision: 1.9 $
//
//==============================================================================

#include "ofc/DFSM.h"

#if _INTERFACE_

#include "ofc/config.h"

#include "ofc/DBitArray.h"
#include "ofc/DList.h"

@class DFSMState;

// The DFSMTrans class implements a transition in the finite state machine. 
// This class is privately used by DFSM. The class stores the trigger and 
// the destination label.

@interface DFSMTrans : Object
{
@private
  DBitArray *_trigger;       // the range of events that trigger the transition
  DFSMState *_destination;   // the destination of the transition (reference !)
}

#endif



@implementation DFSMTrans


//// Constructors

//
// Initialise to a default transition (not allowed)
//
// @return the object
//

- (DFSMTrans *) init
{
  [super init];

  WARNING(DW_METHOD_NOT_ALLOWED, "init");

  return self;
}

//
// Initialise a finite state machine transition (used by DFSM)
//
// @param trigger     the range of events that trigger the transition
// @param destination the destination of the transition
//
// @return the object
//

- (DFSMTrans *) init :(DBitArray *) trigger :(DFSMState *) destination
{
  [super init];

  _trigger     = trigger;
  _destination = destination;
  
  return self;
}


//// Copy related methods

//
// Deepen a copy of the object
//
// @return the object
//

- deepen
{
  [super deepen];
  
  _trigger = [_trigger copy];
    
  return self;
}


//// Deconstructor

//
// Free the object
//
// @return the object
//

- free
{
  if (_trigger != nil)
    [_trigger free];

  _destination = nil;
  
  [super free];

  return self;
}


//// Main method

//
// Feed the transition an event (used by DFSM)
//
// @param event    the event
//
// @return the state (or nil if the transition didn't trigger)
//

- (DFSMState *) feed :(int) event
{
  if ([_trigger has :event])
  {
    return _destination;
  }

  return nil;
}

@end



#if _INTERFACE_

// The DFSMState class implements a state of the finite state machine. The
// class stores the transitions and the any-transition. Special purpose
// states should inherit from FSMState.

@interface DFSMState : Object
{
@private
  DList     *_transitions;   // all the transitions for the state
  DFSMState *_any;           // the any-transition-state (reference)
}

#endif



@implementation DFSMState


//// Constructors

//
// Initialise a finite state machine state
//
// @return the object
//

- (DFSMState *) init
{
  [super init];

  _transitions = [[DList alloc] init];
  _any         = nil;
  
  return self;
}


//// Copy related methods

//
// Deepen a copy of the object
//
// @return the object
//

- deepen
{
  [super deepen];

  _transitions = [_transitions copy];

  return self;
}


//// Deconstructor

//
// Free the object (including the transitions)
//
// @return the object
//

- free
{
  [_transitions free];

  _any = nil;
  
  [super free];

  return self;
}


//// State manipulation

//
// Add a transition to the state (used by DFSM)
//
// @param trigger     the range of events that trigger the transition (or nil)
// @param destination the next state if the transition is triggered
//
// @return the object
//

- (DFSMState *) transition :(DBitArray *) trigger :(DFSMState *) destination
{
  if (trigger == nil)
  {
    if (_any == nil)
    {
      _any = destination;
    }
    else
      WARNING(DW_MEMBER_ALREADY_SET, "any");
  }
  else
  {
    DFSMTrans *trans = [DFSMTrans alloc];

    [_transitions append :[trans init :trigger :destination]];
  }

  return self;
}

//
// Feed the state with an event (used by DFSM)
//
// @param event    the event for the state
//
// @return the next state (or nil if no transition was found for the event)
//

- (DFSMState *) feed :(int) event
{
  DFSMTrans     *trans = nil;
  DFSMState     *state = nil;
  DListIterator *iter  = [DListIterator alloc];
  
  [iter init :_transitions];

  trans = [iter first];
  while ((trans != nil) && (state == nil))
  {
    state = [trans feed :event];
    
    trans = [iter next];
  }
  [iter free];

  if ((state == nil) && (_any != nil))
    state = _any;

  return state;
}

@end



#if _INTERFACE_
  
// The DFSM class implements a (simple) finite state machine. The state machine is
// built by creating a number of transitions for every state. After the state
// machine is built, the processing is started by using the 'start' method.
// From that moment on, events can be fed to the state machine by calling
// 'feed'. This method returns the current state. 
// There is one assumption: events are ints.

@interface DFSM : Object
{
@private
  DFSMState  *_current;   // the current state
  DFSMState  *_previous;  // the previous state
  DList      *_states;    // all known states
}

#endif



@implementation DFSM

//// Constructors

//
// Initialise an empty finite state machine
//
// @return the object
//

- (DFSM *) init
{
  [super init];

  _current  = nil;
  _previous = nil;

  _states  = [DList alloc];

  [_states init];
  
  return self;
}


//// Copy related methods

//
// Deepen a copy of the object
//
// @return the object
//

- deepen
{
  [super deepen];

  _states = [_states copy];

  return self;
}


//// Deconstructor

//
// Free the FSM (but *NOT* the states)
//
// @return the object
//

- shallowFree
{
  _current  = nil;
  _previous = nil;

  [_states shallowFree];

  [super free];

  return self;
}

//
// (Deep) free the FSM (including the states)
//
// @return the object
//

- free
{
  _current  = nil;
  _previous = nil;

  [_states free];

  [super free];

  return self;
}


//// Member methods

//
// Get the current state
//
// @return the current state (or nil for no state)
//

- (DFSMState *) current
{
  return _current;
}

//
// Get the previous state
//
// @return the previous state (or nil for no state)
//

- (DFSMState *) previous
{
  return _previous;
}

//
// Check if the state is changed during the last event
//
// @return is it ?
//

- (BOOL) isChanged
{
  return (_current != _previous);
}


//// Create State machine network

//
// Add a transition to the state machine network. Special case: if trigger is
// nil then this transition is activated if all other transitions fail for an
// event (any-other-transition). Only one such transition is allowed. Note: trigger
// will be freed when FSMState object is freed.
//
// @param origin      the state that has this transition
// @param trigger     the range of events that trigger the transition (or nil)
// @param destination the next state if the transition is triggered
//
// @return the object
//

- (DFSM *) transition :(DFSMState *) origin :(DBitArray *) trigger :(DFSMState *) destination
{
 
  if (![_states has :origin])                        // insure origin state
  {
    [_states append :origin];
  }


  if (![_states has :destination])                 // insure destination state
  {
    [_states append :destination];
  }

  [origin transition :trigger :destination];

  return self;
}


//// Using the state machine

//
// Start the state machine
//
// @param state    the start state
//
// @return the current state (or nil if start event not found)
//

- (DFSMState *) start :(DFSMState *) state
{
  _current = ([_states has :state] ? state : nil);
  
  return _current;  
}

//
// Feed an event to the state machine
//
// @param event    the event for the state machine
//
// @return the current state (or nil for unexpected event)
//

- (DFSMState *) feed :(int) event
{
  if (_current != nil)
  {
    _previous = _current;

    _current = [_current feed :event];
  }

  return _current;
}

@end

/*==========================================================================*/
